/*
 * Copyright 2010 Chad Retz
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package org.jdocng.parse.qdox.function;

import org.jdocng.parse.qdox.QDoxContext;
import org.jdocng.parse.qdox.QDoxUtils;
import org.jdocng.shared.model.DocImpl;
import org.jdocng.shared.model.SourcePositionImpl;
import org.jdocng.shared.model.TagImpl;

import com.sun.javadoc.AnnotationTypeDoc;
import com.sun.javadoc.AnnotationTypeElementDoc;
import com.sun.javadoc.ClassDoc;
import com.sun.javadoc.ConstructorDoc;
import com.sun.javadoc.FieldDoc;
import com.sun.javadoc.MethodDoc;
import com.thoughtworks.qdox.model.AbstractBaseJavaEntity;
import com.thoughtworks.qdox.model.AbstractJavaEntity;
import com.thoughtworks.qdox.model.DocletTag;
import com.thoughtworks.qdox.model.JavaClass;
import com.thoughtworks.qdox.model.JavaField;

/**
 * Apply common properties from {@link AbstractBaseJavaEntity} to {@link DocImpl}
 *
 * @author Chad Retz
 */
public abstract class DocFunction<F extends AbstractBaseJavaEntity, T extends DocImpl> 
        implements ApplyingFunction<F, T> {

    protected DocFunction() {
    }
    
    @Override
    public void apply(QDoxContext context, F from, T to) {
        //TODO check whether tags are removed
        if (from instanceof AbstractJavaEntity) {
            to.setCommentText(((AbstractJavaEntity) from).getComment());
            //TODO parse those inline tags out!
            //TODO no recursion here! so no #setFirstSentenceTags
            to.setFirstSentenceTags(QDoxUtils.getFirstSentenceTags(
                    context, (AbstractJavaEntity) from));
            //TODO check against raw comment!
            to.setRawCommentText(((AbstractJavaEntity) from).getComment());
            to.setPosition(new SourcePositionImpl(from.getLineNumber(), 
                    0, ((AbstractJavaEntity) from).getSource().
                    getURL().toExternalForm()));
        }
        //TODO inline
        to.setName(from.getName());
        to.setAnnotationType(to instanceof AnnotationTypeDoc);
        to.setAnnotationTypeElement(to instanceof AnnotationTypeElementDoc);
        to.setInterfaceInstance(to instanceof ClassDoc && 
                from.getParentClass().isInterface());
        to.setClassInstance(to instanceof ClassDoc && !to.isAnnotationType() &&
                !to.isInterface());
        to.setConstructor(to instanceof ConstructorDoc);
        to.setEnumInstance(to instanceof ClassDoc &&
                from.getParentClass().isEnum());
        to.setField(to instanceof FieldDoc);
        //TODO I don't like this enum constant check
        to.setEnumConstant(to.isField() && from.getParentClass().isEnum() &&
                ((JavaField) from).getType().equals(from.getParentClass().asType()));
        to.setError(to.isClass() && QDoxUtils.
                isInstanceOf((JavaClass) from, "java.lang.Error"));
        to.setException(to.isClass() && QDoxUtils.
                isInstanceOf((JavaClass) from, "java.lang.Exception"));
        to.setMethod(to instanceof MethodDoc && !to.isAnnotationTypeElement());
        to.setOrdinaryClass(to.isClass() && !to.isInterface() &&
                !to.isAnnotationType() && !to.isEnum() && 
                !to.isException() && !to.isError());
        if (from instanceof AbstractJavaEntity) {
            TagImpl[] tags = new TagImpl[((AbstractJavaEntity) from).getTags().length];
            int index = -1;
            for (DocletTag tag : ((AbstractJavaEntity) from).getTags()) {
                tags[++index] = TagFunction.getTagFunction(tag).apply(context, tag);
            }
            to.setTags(tags);
        }
    }

}
